﻿using System;
using System.Collections.Generic;
using System.Xml;
using USU.MetricsMonitor.Common;

namespace USU.MetricsMonitor.LogParser
{
    /// <summary>
    /// Парсер узлов XML-лога
    /// </summary>
    public class RevisionParserFromXml
    {
        /// <summary>
        /// Извлечение информации из узла logentry
        /// </summary>
        /// <param name="node">узел logentry</param>
        /// <returns>информацию о ревизии</returns>
        public static RevisionInfo ParseNode(XmlNode node)
        {
            if (node == null)
            {
                throw new ArgumentNullException("node");
            }
            Revision revision = RevisionNumber(node);
            string author = RevisionAuthor(node);
            DateTime dateTime = RevisionDate(node);
            IEnumerable<ActionInfo> actionsInfo = RevisionActions(node);
            return new RevisionInfo(revision, author, dateTime, actionsInfo);
        }

        /// <summary>
        /// Извлечение информации о номере ревизии
        /// </summary>
        /// <param name="node">узел logentry</param>
        /// <returns>номер ревизии</returns>
        private static Revision RevisionNumber(XmlNode node)
        {
            node = node.Attributes.GetNamedItem("revision");
            if (node == null)
            {
                throw new LogParserException("Не найдено значение атрибута revision узла logentry");
            }
            try
            {
                return new Revision(node.Value);
            }
            catch (Exception exception)
            {
                throw new LogParserException("Некорректный номер ревизии", exception);
            }
        }

        /// <summary>
        /// Извлечение информации о авторе ревизии
        /// </summary>
        /// <param name="node">узел logentry</param>
        /// <returns>автор ревизии</returns>
        private static string RevisionAuthor(XmlNode node)
        {
            node = node.SelectSingleNode("author");
            if (node == null)
            {
                return "";
            }
            string author = node.InnerText;
            if (author == "")
            {
                throw new LogParserException("Не найдено содержимое узла <author>");
            }
            return author;
        }

        /// <summary>
        /// Получение даты ревизии
        /// </summary>
        /// <param name="node">узел logentry</param>
        /// <returns>дата ревизии</returns>
        private static DateTime RevisionDate(XmlNode node)
        {
            node = node.SelectSingleNode("date");
            if (node == null)
            {
                throw new LogParserException("Не найден узел <date>");
            }
            DateTime revisionDateTime;
            try
            {
                revisionDateTime = DateTime.Parse(node.InnerText);
            }
            catch (Exception exception)
            {
                throw new LogParserException("Некорректная дата", exception);
            }
            return revisionDateTime.ToUniversalTime();
        }

        /// <summary>
        /// Извлечение информации о действиях ревизии
        /// </summary>
        /// <param name="node">узел logentry</param>
        /// <returns>коллекция информации о действиях</returns>
        private static IEnumerable<ActionInfo> RevisionActions(XmlNode node)
        {
            node = node.SelectSingleNode("paths");
            if (node == null)
            {
                return new List<ActionInfo>();
            }
            XmlNodeList nodeListPath = node.SelectNodes("path");
            if (nodeListPath == null)
            {
                throw new LogParserException("Не найдено ни одного подузла <path> в узле <paths>");
            }
            List<ActionInfo> actions = new List<ActionInfo>();
            foreach (XmlNode nodePath in nodeListPath)
            {
                string path = nodePath.InnerText;
                if (path == "")
                {
                    throw new LogParserException("Не найдено содержимое узла <path>");
                }
                XmlAttributeCollection attributes = nodePath.Attributes;
                XmlNode nodeKind = attributes.GetNamedItem("kind");
                if (nodeKind == null)
                {
                    throw new LogParserException("Не найден атрибут kind узла <path>");
                }
                Kind kind;
                switch (nodeKind.Value)
                {
                    case "dir":
                        kind = Kind.DIR;
                        break;
                    case "":
                        kind = Kind.EMPTY;
                        break;
                    case "file":
                        kind = Kind.FILE;
                        break;
                    default:
                        throw new LogParserException("Некорректное значение атрибута kind узла <path>");
                }
                XmlNode nodeAction = attributes.GetNamedItem("action");
                if (nodeAction == null)
                {
                    throw new LogParserException("Не найден атрибут action узла <path>");
                }
                Action action;
                switch (nodeAction.Value)
                {
                    case "A":
                        action = Action.ADDED;
                        break;
                    case "D":
                        action = Action.DELETED;
                        break;
                    case "M":
                        action = Action.MODIFIED;
                        break;
                    case "R":
                        action = Action.REPLACED;
                        break;
                    default:
                        throw new LogParserException("Некорректное значение атрибута action узла <path>");
                }
                ActionInfo actionInfo = new ActionInfo(kind, action, path);
                actions.Add(actionInfo);
            }
            return actions;
        }
    }
}